package cn.bugstack.domain.strategy.service.armory;

import cn.bugstack.domain.strategy.model.entity.StrategyAwardEntity;
import cn.bugstack.domain.strategy.model.entity.StrategyEntity;
import cn.bugstack.domain.strategy.model.entity.StrategyRuleEntity;
import cn.bugstack.domain.strategy.repositroy.IStrategyRepository;
import cn.bugstack.types.enums.ResponseCode;
import cn.bugstack.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.*;

@Service
@Slf4j
public class StrategyArmoryDispatch implements IStrategyArmory,IStrategyDispatch {

	@Resource
	private IStrategyRepository repository;

	@Override
	public boolean assembleLotteryStrategy(Long strategyId) {
		// 1. 查询策略配置
		List<StrategyAwardEntity> strategyAwardEntities = repository.queryStrategyAwardList(strategyId);
		assembleLotteryStrategy(String.valueOf(strategyId), strategyAwardEntities);

		// 2. 权重策略配置 - 适用于 rule_weight 权重规则配置
		StrategyEntity strategyEntity = repository.queryStrategyEntityByStrategyId(strategyId);
		String ruleWeight = strategyEntity.getRuleWeight();
		if (null == ruleWeight) return true;

		StrategyRuleEntity strategyRuleEntity = repository.queryStrategyRule(strategyId, ruleWeight);
		if (null == strategyRuleEntity) {
			throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(), ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
		}
		Map<String, List<Integer>> ruleWeightValueMap = strategyRuleEntity.getRuleWeightValues();
		Set<String> keys = ruleWeightValueMap.keySet();
		for (String key : keys) {
			List<Integer> ruleWeightValues = ruleWeightValueMap.get(key);
			ArrayList<StrategyAwardEntity> strategyAwardEntitiesClone = new ArrayList<>(strategyAwardEntities);
			strategyAwardEntitiesClone.removeIf(entity -> !ruleWeightValues.contains(entity.getAwardId()));
			assembleLotteryStrategy(String.valueOf(strategyId).concat("_").concat(key), strategyAwardEntitiesClone);
		}

		return true;
	}

	public void assembleLotteryStrategy(String key,List<StrategyAwardEntity> strategyAwardEntities){
		//获取最小值概率  拿到所有的awardRate进行比较，没有最小值时返回0
//		BigDecimal minAwardRate = strategyAwardEntities.stream()
//				.filter(Objects::nonNull)
//				.map(StrategyAwardEntity::getAwardRate)
//				.filter(Objects::nonNull)
//				.min(BigDecimal::compareTo)
//				.orElse(BigDecimal.ZERO);
		BigDecimal minAwardRate = strategyAwardEntities.stream()
				.map(StrategyAwardEntity :: getAwardRate)
				.min(BigDecimal::compareTo)
				.orElse(BigDecimal.ZERO);
		//2.获取概率值总和，Stream.reduce()是java8引入的流式操作聚合函数，可处理累加、最大值、最小值、拼接等逻辑
		BigDecimal totalRate = strategyAwardEntities.stream()
				.map(StrategyAwardEntity :: getAwardRate)
				.reduce(BigDecimal.ZERO,BigDecimal::add);
		// 获取总概率，安全处理 null
//		BigDecimal totalRate = strategyAwardEntities.stream()
//				.map(entity -> Optional.ofNullable(entity.getAwardRate()).orElse(BigDecimal.ZERO))
//				.reduce(BigDecimal.ZERO, BigDecimal::add);
		//3.用总概率%最小概率可获取概率范围，百分位、千分位、万分位
		//totalRate使用divide方法除以minAwardRate，指定精度为0，即取整，RoundingMode.CEILING表示向上取整
		BigDecimal rateRange = totalRate.divide(minAwardRate,0, RoundingMode.CEILING);
		List<Integer> strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue());
		/*4.遍历每一个奖品配置，根据其概率，在 strategyAwardSearchRateTables 列表中重
		复添加该奖品的 ID，使得后续可以通过随机数索引快速找到对应奖品。
		 */
		for(StrategyAwardEntity strategyAward : strategyAwardEntities){
			Integer awardId = strategyAward.getAwardId();
			BigDecimal awardRate = strategyAward.getAwardRate();
			//计算出每个概率值需要存放到查找表的数量并循环填充，multiply用于乘法运算，setScale保留0位小数并向上取整
			for (int i = 0; i < rateRange.multiply(awardRate).setScale(0, RoundingMode.CEILING).intValue() ;i++) {
				strategyAwardSearchRateTables.add(awardId);
			}
		}
		//5.将strategyAwardSearchRateTables乱序排序
		Collections.shuffle(strategyAwardSearchRateTables);
		//6.乱选排序后放入到map中
		HashMap<Integer,Integer> shuffleStrategyAwardSearchRateTables = new HashMap<>();
		for (int i = 0; i < strategyAwardSearchRateTables.size(); i++) {
			shuffleStrategyAwardSearchRateTables.put(i, strategyAwardSearchRateTables.get(i));
		}
		//7.存储到redis中
		repository.storeStrategyAwardSearchRateTables(key,shuffleStrategyAwardSearchRateTables.size(),shuffleStrategyAwardSearchRateTables);
	}

	@Override
	public Integer getRandomAwardId(Long strategyId) {
		//分布式部署下，不一定为当前应用做的策略装配，也就是值不一定会保存到本应用，而是分布式应用，所以需要从Redis中获取
		int rateRange = repository.getRateRange(strategyId);
		//通过生成的随机值，获取概率值奖品查找表结果
		return repository.getStrategyAwardAssemble(String.valueOf(strategyId),new SecureRandom().nextInt(rateRange));
	}

	@Override
	public Integer getRandomAwardId(Long strategyId, String ruleWeightValue) {
		String key =String.valueOf(strategyId).concat("_").concat(ruleWeightValue);
		//分布式部署下，不一定为当前应用做的策略装配，也就是值不一定会保存到本应用，而是分布式应用，所以需要从Redis中获取
		int rateRange = repository.getRateRange(key);
		//通过生成的随机值，获取概率值奖品查找表结果
		return repository.getStrategyAwardAssemble(key,new SecureRandom().nextInt(rateRange));
	}

}


